﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web.UI;
using BSpec.Specification;

namespace BSpec.Document
{
    public class HtmlRenderer : IDocumentRenderer
    {
        private const string HtmlExtension = ".html";

        public string FileExtension
        {
            get { return HtmlExtension; }
        }

        public string Render(SpecificationDataSet specificationDataset)
        {
            var builder = new StringBuilder();
            using (var writer = new HtmlTextWriter(new StringWriter(builder)))
            {
                RenderHeader(writer, specificationDataset.Name);
                RenderContent(specificationDataset, writer);
                RenderFooter(writer);
            }
            return builder.ToString();
        }

        private void RenderHeader(HtmlTextWriter writer, string pageTitle)
        {
            writer.RenderBeginTag(HtmlTextWriterTag.Html);
            writer.RenderBeginTag(HtmlTextWriterTag.Head);
            writer.RenderBeginTag(HtmlTextWriterTag.Title);
            writer.Write(String.Format("Specifications for {0}", pageTitle));
            writer.RenderEndTag();
            RenderStyles(writer);
            writer.RenderEndTag();
            writer.RenderBeginTag(HtmlTextWriterTag.Body);
        }

        private void RenderStyles(HtmlTextWriter writer)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/css");
            writer.RenderBeginTag(HtmlTextWriterTag.Style);
            writer.Write(StyleSheet);
            writer.RenderEndTag();
        }

        private void RenderFooter(HtmlTextWriter writer)
        {
            writer.RenderBeginTag(HtmlTextWriterTag.Hr);
            writer.RenderEndTag();

            RenderFooterMessage(writer);

            writer.RenderEndTag();
            writer.RenderEndTag();
        }

        private void RenderFooterMessage(HtmlTextWriter writer)
        {
            writer.RenderBeginTag(HtmlTextWriterTag.P);
            writer.RenderBeginTag(HtmlTextWriterTag.I); 
            writer.Write("Generated by BSpec ");
            writer.AddAttribute(HtmlTextWriterAttribute.Href, "http://code.google.com/p/bspec/");
            writer.AddAttribute(HtmlTextWriterAttribute.Target, "_blank");
            writer.RenderBeginTag(HtmlTextWriterTag.A);
            writer.Write("http://code.google.com/p/bspec/");
            writer.RenderEndTag();
            writer.RenderEndTag();
            writer.RenderEndTag();
        }

        private void RenderContent(SpecificationDataSet specificationDataset, HtmlTextWriter writer)
        {
            RenderHeading(specificationDataset, writer);
            RenderTableOfContents(specificationDataset.Concerns, writer);
            RenderConcerns(specificationDataset.Concerns, writer);
        }

        private void RenderHeading(SpecificationDataSet specificationDataset, HtmlTextWriter writer)
        {
            writer.RenderBeginTag(HtmlTextWriterTag.H1);
            writer.Write(specificationDataset.Name);
            writer.RenderEndTag();
        }

        public static void RenderTableOfContents(IEnumerable<Concern> concerns, HtmlTextWriter writer)
        {
            if (concerns.Count() == 0) return;

            writer.RenderBeginTag(HtmlTextWriterTag.H2);
            writer.Write("Table of contents");
            writer.RenderEndTag();
            writer.RenderBeginTag(HtmlTextWriterTag.Ol);
            foreach (var concern in concerns.OrderBy(c => c.Name))
            {
                writer.RenderBeginTag(HtmlTextWriterTag.Li);
                writer.AddAttribute(HtmlTextWriterAttribute.Href, string.Format("#{0}", concern.Name.RemoveWhiteSpace()));
                writer.RenderBeginTag(HtmlTextWriterTag.A);
                writer.Write(string.Format("{0} specifications", concern.Name));
                writer.RenderEndTag();
                writer.RenderEndTag();
            }
            writer.RenderEndTag();
        }

        public static void RenderConcerns(IEnumerable<Concern> concerns, HtmlTextWriter writer)
        {
            int count = 1;
            foreach (var concern in concerns.OrderBy(c => c.Name))
            {
                RenderConcern(concern, count, writer);
                RenderScenarios(concern.Scenarios, writer);
                count++;
            }
        }

        private static void RenderConcern(Concern concern, int index, HtmlTextWriter writer)
        {
            if (concern is NullConcern) return;

            RenderConcernAnchor(concern, writer);
            writer.RenderBeginTag(HtmlTextWriterTag.H2);
            writer.Write(string.Format("{0}. {1} specifications", index, concern.Name));
            writer.RenderEndTag();
        }

        private static void RenderConcernAnchor(Concern concern, HtmlTextWriter writer)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Name, concern.Name.RemoveWhiteSpace());
            writer.RenderBeginTag(HtmlTextWriterTag.A);
            writer.Write("&nbsp;");
            writer.RenderEndTag();
        }

        public static void RenderScenarios(IEnumerable<Scenario> scenarios, HtmlTextWriter writer)
        {
            foreach (var scenario in scenarios)
            {
                RenderScenario(scenario, writer);
                RenderContexts(scenario.Contexts, writer);
            }
        }

        private static void RenderScenario(Scenario scenario, HtmlTextWriter writer)
        {
            if (scenario is NullScenario) return;

            writer.RenderBeginTag(HtmlTextWriterTag.H3);
            writer.Write(string.Format("Scenario: {0}", scenario.Name));
            writer.RenderEndTag();
        }

        public static void RenderContexts(IEnumerable<Context> contexts, HtmlTextWriter writer)
        {
            if (contexts.Count() == 0) return;

            writer.RenderBeginTag(HtmlTextWriterTag.Ul);
            foreach (var context in contexts)
            {
                RenderContext(context, writer);
            }
            writer.RenderEndTag();
        }

        private static void RenderContext(Context context, HtmlTextWriter writer)
        {
            if (context is NullContext)
            {
                RenderBehaviours(context.Behaviours, writer);
            }
            else
            {
                writer.RenderBeginTag(HtmlTextWriterTag.Li);
                writer.Write(context.Name);
                RenderBehaviours(context.Behaviours, writer);
                writer.RenderEndTag();
            }
        }

        public static void RenderBehaviours(IEnumerable<Behaviour> behaviours, HtmlTextWriter writer)
        {
            if (behaviours.Count() == 0) return;

            writer.RenderBeginTag(HtmlTextWriterTag.Ul);
            foreach (var behaviour in behaviours)
            {
                RenderBehaviour(behaviour, writer);
            }
            writer.RenderEndTag();
        }

        private static void RenderBehaviour(Behaviour behaviour, HtmlTextWriter writer)
        {
            writer.RenderBeginTag(HtmlTextWriterTag.Li);
            writer.Write(behaviour.Name);
            RenderObservations(behaviour.Observations, writer);
            writer.RenderEndTag();
        }

        public static void RenderObservations(IList<Observation> observations, HtmlTextWriter writer)
        {
            if (observations.Count() == 0) return;

            writer.RenderBeginTag(HtmlTextWriterTag.Ul);
            foreach (var observation in observations)
            {
                RenderObservation(observation, writer);
            }
            writer.RenderEndTag();
        }

        private static void RenderObservation(Observation observation, HtmlTextWriter writer)
        {
            writer.RenderBeginTag(HtmlTextWriterTag.Li);
            writer.Write(observation.Name);
            writer.RenderEndTag();
        }

        #region StyleSheet

        private const string StyleSheet = @"body
{
	color: black;
	font-family: sans-serif;
	font-size: small;
}
h1
{
	font-size: 2.3em;
	line-height: 1.2em;
	margin-bottom: 0.1em;
	padding-bottom: 0;
}
h2
{
	font-size: 1.8em;	
	border-top: 1px solid #AAAAAA;
	margin-bottom: 0.6em;
}
h3
{
	font-size: 1.4em;
	font-weight:bold;
}
h1, h2, h3
{
	color: black;
	font-weight: normal;
	margin: 0;
	padding-bottom: 0.17em;
	padding-top: 0.5em;
}
ul
{
	line-height: 1.5em;

}
li
{
	margin-bottom: 0.1em;
}";

        #endregion
    }
}
